﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

// предлагается закодить 3 класса, первые два для 1 уравнения, другой для систем
// не забыть контроль погрешности - схема 2с1/2ш
// Вывод - графики, и стандартные таблицы
// переменные: порядок, нач приближение, массивы текущих приближений, макс число итераций, номер текущей итерации
// для тестовой задачи - массив ответов, точность 2с1/2ш
// Функции: вычисление правой части, сделать 1 шаг (РК 2/4 порядка), 2с1/2ш, 
namespace Lab2_1
{
  class testEqation
  {
    #region Переменные

    // порядок
    public int p = 4;

    // счётчики максимального и текущего числа итераций
    public int nMax = -1;
    public int n = -1;

    // шаг и начальное приближение (x0,u0)
    public double h = 0;
    public double u0;
    public double x0 = 0;

    // абсцисса и ордината приближений и приближений половинного счёта
    public double[] x, v, v2, S, hArray;

    // параметр контроля схемы 2с1/2ш
    public double eps;

    // cчётчики удвоения и ополовинивания шага
    public int dNumber;
    public int hNumber;

    // максимумы и минимумы для вывода 
    public double maxH, maxHX;
    public double minH, minHX;
    public double maxAbs, maxAbsX;
    public double maxS;

    // правая граница
    public double rightBorder;


    #endregion

    // в тестовой задаче начальные точки траекторий -1 и 1
    public void createTestEqation(int NMAX, double HSTART, double U0, double EPS, double RIGHTBORDER)
    {
      nMax = NMAX;
      h = HSTART;
      u0 = U0;
      eps = EPS;
      rightBorder = RIGHTBORDER;

      x = new double[10000];
      v = new double[10000];
      v2 = new double[10000];
      S = new double[10000];
      hArray = new double[10000];

      n = 0;
      x[0] = x0;
      v[0] = U0;
      v2[0] = U0;

      hNumber = 0;
      dNumber = 0;
      S[0] = 0;

      maxAbs = -1.0;
      maxAbsX = -1.0;
      maxH = h;
      maxHX = x[n];
      maxS = -1.0;
      minH = h;
      minHX = x[n];


    }

    ~testEqation()
    {
      x = null;
      v = null;
      v2 = null;
      S = null;
      hArray = null;
    }

    // вычислить правую часть тестовой задачи
    public double getRight(double x, double u)
    {
      return 2 * u;
    }

    // сделать один шаг метода
    public void doSteps()
    {
      while (x[n] < rightBorder)
      {
        double V1 = computeNextPoint(x[n], h, v[n]);
        double V2 = computeNextPoint(x[n] + h / 2, h / 2,
          computeNextPoint(x[n], h / 2, v[n]));
        double temp = Math.Pow(2.0, p);
        double Loc = ((V2 - V1) * temp / (temp - 1));
        if ((Math.Abs(Loc) < eps) && (Math.Abs(Loc) > eps / Math.Pow(2.0, p + 1)))
        {
          n++;
          v[n] = V1;
          v2[n] = V2;
          x[n] = x[n - 1] + h;
          S[n] = Loc;
          hArray[n] = h;
          if (S[n] > maxS)
          {
            maxS = S[n];
          }

        }
        if (Math.Abs(Loc) > eps)
        {
          h = h / 2;
          hNumber++;
          if (h < minH)
          {
            minH = h;
            minHX = x[n];
          }
        }
        if (Math.Abs(Loc) < eps / Math.Pow(2.0, p + 1))
        {
          n++;
          v[n] = V1;
          v2[n] = V2;
          x[n] = x[n - 1] + h;
          S[n] = Loc;
          hArray[n] = h;
          if (S[n] > maxS)
          {
            maxS = S[n];
          }
          h = h * 2;
          dNumber++;
          if (h > maxH)
          {
            maxH = h;
            maxHX = x[n];
          }
        }
      }
    }
    // просчитать следующую точку (Рунге Кутт 4 порядка)
    double computeNextPoint(double startX, double h, double startU)
    {
      double k1 = h * getRight(startX, startU);
      double k2 = h * getRight(startX + h / 2, startU + k1 / 2);
      double k3 = h * getRight(startX + h / 2, startU + k2 / 2);
      double k4 = h * getRight(startX + h, startU + k3);
      return startU + (1.0 / 6.0) * (k1 + 2 * k2 + 2 * k3 + k4);
    }

    // вернуть истинное значение функции
    public double getAnswer(double x)
    {
      return u0 * Math.Exp(2 * x);
    }

    // найти максимальное отклонение численной траектории от истинной
    public void findMaxDifference()
    {
      for (int i = 0; i <= n; i++)
      {
        if (Math.Abs(getAnswer(x[n]) - v[n]) > maxAbs)
        {
          maxAbs = Math.Abs(getAnswer(x[n]) - v[n]);
          maxAbsX = x[n];
        }
      }
    }
  }
}
